package com.shiroexploit.gui;

import com.shiroexploit.task.GetDNSLogRecordTask;
import com.shiroexploit.util.Config;
import com.shiroexploit.util.HttpRequest;
import com.shiroexploit.util.PayloadType;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

public class ConfigPane {
    private VBox vBox = new VBox();
    private StartPane previousPane;
    private ToggleGroup typeGroup = new ToggleGroup();
    private RadioButton ceyeButton = new RadioButton("使用 ceye.io 进行漏洞检测");
    private RadioButton dnsLogButton = new RadioButton("使用 dnslog.cn 进行漏洞检测");
    private RadioButton oobServiceButton = new RadioButton("使用 JRMP + dnslog.cn 进行漏洞检测");
    private RadioButton echoButton = new RadioButton("使用回显进行漏洞检测");
    private GridPane middlePane = new GridPane();
    private TextField ipAddress = new TextField();
    private TextField httpServicePort = new TextField();
    private TextField JRMPListenerPort = new TextField();
    private GridPane bottomPane = new GridPane();
    private ToggleGroup skipIfFound = new ToggleGroup();
    private RadioButton yesForSkip = new RadioButton("是");
    private RadioButton noForSkip = new RadioButton("否");
    Button previous = new Button("上一步");
    Button next = new Button("下一步");
    private TextField staticFilePathField = new TextField();
    private boolean used = false;

    public void update(){
        // 等于0表示使用的是ceye
        if(Config.getInstance().getCheckMethod() == 0){
            typeGroup.selectToggle(ceyeButton);

            middlePane.setDisable(true);
            bottomPane.setDisable(true);
            staticFilePathField.setDisable(true);
        }else if(Config.getInstance().getCheckMethod() == 1){
            typeGroup.selectToggle(dnsLogButton);

            middlePane.setDisable(true);
            bottomPane.setDisable(true);
            staticFilePathField.setDisable(true);
        } else if(Config.getInstance().getCheckMethod() == 3){
            typeGroup.selectToggle(echoButton);
            staticFilePathField.setText(Config.getInstance().getStaticFilePath());

            middlePane.setDisable(true);
            bottomPane.setDisable(true);
            staticFilePathField.setDisable(false);
        }else{
            typeGroup.selectToggle(oobServiceButton);
            staticFilePathField.setDisable(true);

            String address = Config.getInstance().getJRMPServiceAddress();
            if(address != null && !address.equals("")){
                ipAddress.setText(address);
            }

            int httpPort = Config.getInstance().getHTTPServicePort();
            if(httpPort != 0){
                httpServicePort.setText(httpPort + "");
            }

            int JRMPPort = Config.getInstance().getHTTPServicePort();
            if(JRMPPort != 0){
                httpServicePort.setText(JRMPPort + "");
            }


            boolean flag = Config.getInstance().isSkipIfFound();
            if(flag){
                skipIfFound.selectToggle(yesForSkip);
            }else{
                skipIfFound.selectToggle(noForSkip);
            }
        }
    }

    public ConfigPane(StartPane pane){
        this.previousPane = pane;
        drawPane();
        addListeners();
    }

    public Pane getPane(){
        return this.vBox;
    }

    private void addListeners(){

        typeGroup.selectedToggleProperty().addListener(new ChangeListener<Toggle>() {
            @Override
            public void changed(ObservableValue<? extends Toggle> observable, Toggle oldValue, Toggle newValue) {
                if(newValue == ceyeButton){
                    middlePane.setDisable(true);
                    bottomPane.setDisable(true);
                    staticFilePathField.setDisable(true);

                }else if(newValue == dnsLogButton){
                    middlePane.setDisable(true);
                    bottomPane.setDisable(true);
                    staticFilePathField.setDisable(true);

                }else if(newValue == echoButton){
                    middlePane.setDisable(true);
                    bottomPane.setDisable(true);
                    staticFilePathField.setDisable(false);

                    if(!used){
                        String[] names = {"CommonsBeanutils1","CommonsCollections2","CommonsCollections3","CommonsCollections4","CommonsCollections8",
                                "CommonsCollections10", "Jdk7u21","Hibernate1","Spring1","Spring2","JBossInterceptors1","JSON1","JavassistWeld1",
                                "MozillaRhino1","MozillaRhino2","ROME","Vaadin1"};
                        List<PayloadType> types = new ArrayList<>();
                        for(String name : names){
                            types.add(PayloadType.valueOf(name));
                        }

                        types.retainAll(Config.getInstance().getGadgets());
                        Config.getInstance().setGadgets(types);

                        used = true;
                    }
                }else {
                    middlePane.setDisable(false);
                    bottomPane.setDisable(false);
                    staticFilePathField.setDisable(true);
                }
            }
        });

        previous.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                Stage currentStage = (Stage)vBox.getScene().getWindow();
                currentStage.close();
                Stage previous = (Stage)previousPane.getPane().getScene().getWindow();
                previous.show();
            }
        });

        next.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                if(typeGroup.getSelectedToggle() == ceyeButton){
                    Config.getInstance().setCheckMethod(0);
                    nextStage();
                }

                if(typeGroup.getSelectedToggle() == dnsLogButton){
                    Stage currentStage = (Stage)vBox.getScene().getWindow();
                    GetDNSLogRecordTask task = new GetDNSLogRecordTask(Config.getInstance());
                    task.valueProperty().addListener(new ChangeListener<Integer>() {
                        @Override
                        public void changed(ObservableValue<? extends Integer> observable, Integer oldValue, Integer newValue) {
                            if(task.getStatus() == 1){
                                Config.getInstance().setCheckMethod(1);
                                nextStage();
                            }

                            if(task.getStatus() == -1){
                                PromptMessageUI.getAlert("DNSLog无法访问","DNSLog.cn暂时无法访问，请稍后再试!");
                            }
                        }
                    });

                    PenddingUI penddingUI = new PenddingUI(task, currentStage);
                    penddingUI.activateProgressBar();
                }

                if(typeGroup.getSelectedToggle() == echoButton){

                    if(Config.getInstance().getGadgets().size() == 0){
                        PromptMessageUI.getAlert("输入错误","您指定的 Gadget 无法用于回显，请重新选择");
                        return;
                    }

                    Config.getInstance().setCheckMethod(3);

                    String staticFilePath = staticFilePathField.getText();
                    if(staticFilePath == null || staticFilePath.trim().equals("")){
                        Config.getInstance().setStaticFilePath(Config.getInstance().getRequestInfo().getRequestURL());
                    }else{
                        try {
                            URL url = new URL(staticFilePath);
                        } catch (MalformedURLException e) {
                            PromptMessageUI.getAlert("输入错误","输入的静态资源 URL 不是一个有效的 URL");
                            return;
                        }

                        Config.getInstance().setStaticFilePath(staticFilePath);
                    }

                    nextStage();
                }

                if(typeGroup.getSelectedToggle() == oobServiceButton){

                    String ip = ipAddress.getText();
                    String httpPort = httpServicePort.getText();
                    String JRMPPort = JRMPListenerPort.getText();

                    if(ip == null || ip.trim().equals("")){
                        PromptMessageUI.getAlert("输入错误","IP地址不能为空");
                        return;
                    }

                    if(httpPort == null || httpPort.trim().equals("")){
                        PromptMessageUI.getAlert("输入错误","HTTPService端口不能为空");
                        return;
                    }

                    if(JRMPPort == null || JRMPPort.trim().equals("")){
                        PromptMessageUI.getAlert("输入错误","JRMPListener端口不能为空");
                        return;
                    }

                    if(!validatePortInput(httpPort, JRMPPort)){
                        PromptMessageUI.getAlert("输入错误","您输入的端口格式不正确");
                        return;
                    }

                    if(!validateOOBServerConnection(ip, Integer.parseInt(httpPort))){
                        PromptMessageUI.getAlert("连接失败","无法连接到您指定的OOB Server，请确定配置是否正确");
                        return;
                    }

                    Stage currentStage = (Stage)vBox.getScene().getWindow();
                    GetDNSLogRecordTask task = new GetDNSLogRecordTask(Config.getInstance());
                    task.valueProperty().addListener(new ChangeListener<Integer>() {
                        @Override
                        public void changed(ObservableValue<? extends Integer> observable, Integer oldValue, Integer newValue) {
                            if(task.getStatus() == 1){
                                Config.getInstance().setCheckMethod(2);
                                Config.getInstance().setJRMPServiceAddress(ip);
                                Config.getInstance().setHTTPServicePort(Integer.parseInt(httpPort));
                                Config.getInstance().setJRMPServicePort(Integer.parseInt(JRMPPort));
                                Config.getInstance().setSkipIfFound(skipIfFound.getSelectedToggle() == yesForSkip ? true : false);
                                nextStage();
                            }

                            if(task.getStatus() == -1){
                                PromptMessageUI.getAlert("DNSLog无法访问","DNSLog.cn暂时无法访问，请稍后再试!");
                            }
                        }
                    });

                    PenddingUI penddingUI = new PenddingUI(task, currentStage);
                    penddingUI.activateProgressBar();
                }
            }
        });
    }

    public void drawPane(){
        ceyeButton.setToggleGroup(typeGroup);
        dnsLogButton.setToggleGroup(typeGroup);
        oobServiceButton.setToggleGroup(typeGroup);
        echoButton.setToggleGroup(typeGroup);

        vBox.getChildren().addAll(ceyeButton, dnsLogButton, oobServiceButton);
        vBox.setMargin(ceyeButton, new Insets(20,0,0,40));
        vBox.setMargin(dnsLogButton, new Insets(20,0,0,40));
        vBox.setMargin(oobServiceButton, new Insets(20,0,0,40));

        middlePane.setVgap(20);
        middlePane.setHgap(20);

        Label labelForIP = new Label("IPAddress");
        middlePane.add(labelForIP,0, 1);
        middlePane.add(ipAddress, 1,1);

        Label labelForHTTPPort = new Label("HTTPService Port");
        httpServicePort.setText("8080");
        middlePane.add(labelForHTTPPort,0, 2);
        middlePane.add(httpServicePort, 1,2);

        Label labelForJRMPPort = new Label("JRMPListener Port");
        JRMPListenerPort.setText("8088");
        middlePane.add(labelForJRMPPort,0, 3);
        middlePane.add(JRMPListenerPort, 1,3);
        middlePane.setDisable(true);

        vBox.getChildren().add(middlePane);
        vBox.setMargin(middlePane, new Insets(0, 100, 0,80));

        bottomPane.setVgap(20);
        bottomPane.setHgap(20);

        Label label1 = new Label("找到可用Gadget即停止寻找");
        yesForSkip.setToggleGroup(skipIfFound);
        noForSkip.setToggleGroup(skipIfFound);
        skipIfFound.selectToggle(yesForSkip);
        bottomPane.add(label1, 0, 0);
        bottomPane.add(yesForSkip, 1, 0);
        bottomPane.add(noForSkip, 2,0);

        bottomPane.setDisable(true);

        vBox.getChildren().add(bottomPane);
        vBox.setMargin(bottomPane, new Insets(20, 100, 20,80));

        vBox.getChildren().addAll(echoButton, staticFilePathField);
        vBox.setMargin(echoButton, new Insets(0,0,0,40));
        vBox.setMargin(staticFilePathField, new Insets(20,20,0,20));
        staticFilePathField.setPromptText("(可选)请输入一个静态资源URL,如 http://www.xxx.com/static/logo.png");

        HBox hBox = new HBox();
        hBox.getChildren().addAll(previous, next);
        hBox.setMargin(previous, new Insets(20,20,0,0));
        hBox.setMargin(next, new Insets(20,0,0,0));
        hBox.setAlignment(Pos.CENTER);
        vBox.getChildren().add(hBox);
    }

    private boolean validateOOBServerConnection(String ip, int port) {
        String url = "http://" + ip + ":" + port + "/echo";
        String result = null;
        try{
            result = HttpRequest.getResponse(url);
        }catch (IOException e){
            return false;
        }

        if(result != null && result.trim().equals("OK")){
            return true;
        }

        return false;
    }

    private boolean validatePortInput(String httpServicePort, String JRMPListener){
        try{
            Integer.parseInt(httpServicePort);
            Integer.parseInt(JRMPListener);
        }catch(NumberFormatException e){
            return false;
        }

        return true;
    }

    private void nextStage(){
        Stage newStage = new Stage();
        MainPane mainPane = new MainPane();
        newStage.setTitle(Config.getInstance().getRequestInfo().getRequestURL());
        newStage.setScene(new Scene(mainPane.getPane(), 800,600));
        //close第一步和第二步的2个界面
        Stage currentStage = (Stage)vBox.getScene().getWindow();
        currentStage.hide();
        Stage previousStage = (Stage)previousPane.getPane().getScene().getWindow();
        previousStage.close();
        newStage.setOnShown(new EventHandler<WindowEvent>() {
            @Override
            public void handle(WindowEvent event) {
                Platform.runLater(new Runnable() {
                    @Override
                    public void run() {
                        mainPane.startRuning();
                    }
                });
            }
        });

        //关闭 Stage 时结束所有正在执行的任务
        //参考 https://stackoverflow.com/questions/12153622/how-to-close-a-javafx-application-on-window-close
        newStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
            @Override
            public void handle(WindowEvent event) {
                Platform.exit();


                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Runtime.getRuntime().exec("java -jar ShiroExploit.jar");
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();

                System.exit(0);
            }
        });

        newStage.show();
    }
}
